/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 * Origin source from MaNGOS(GPL2).
 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
 */

#include "Threading.h"

#include <vector>

#include "ace/OS_NS_unistd.h"
#include "ace/Sched_Params.h"

namespace PQCore
{

	ThreadPriority::ThreadPriority()
	{
		for (int i = Idle; i < MAXPRIORITYNUM; ++i)
			_priority[i] = ACE_THR_PRI_OTHER_DEF;

		_priority[Idle] = ACE_Sched_Params::priority_min(ACE_SCHED_OTHER);
		_priority[Realtime] = ACE_Sched_Params::priority_max(ACE_SCHED_OTHER);

		std::vector<int> _tmp;

		ACE_Sched_Params::Policy _policy = ACE_SCHED_OTHER;
		ACE_Sched_Priority_Iterator pr_iter(_policy);

		while (pr_iter.more())
		{
			_tmp.push_back(pr_iter.priority());
			pr_iter.next();
		}

		assert (!_tmp.empty());

		if(_tmp.size() >= MAXPRIORITYNUM)
		{
			const size_t max_pos = _tmp.size();
			size_t min_pos = 1;
			size_t norm_pos = 0;
			for (size_t i = 0; i < max_pos; ++i)
			{
				if(_tmp[i] == ACE_THR_PRI_OTHER_DEF)
				{
					norm_pos = i + 1;
					break;
				}
			}

			//since we have only 7(seven) values in enum Priority
			//and 3 we know already (Idle, Normal, Realtime) so
			//we need to split each list [Idle...Normal] and [Normal...Realtime]
			//into ?piesces
			const size_t _divider = 4;
			size_t _div = (norm_pos - min_pos) / _divider;
			if(_div == 0)
				_div = 1;

			min_pos = (norm_pos - 1);

			_priority[Low] = _tmp[min_pos -= _div];
			_priority[Lowest] = _tmp[min_pos -= _div ];

			_div = (max_pos - norm_pos) / _divider;
			if(_div == 0)
				_div = 1;

			min_pos = norm_pos - 1;

			_priority[High] = _tmp[min_pos += _div];
			_priority[Highest] = _tmp[min_pos += _div];
		}
	}

	int ThreadPriority::getPriority(Priority p) const
	{
		if(p < Idle)
			p = Idle;

		if(p > Realtime)
			p = Realtime;

		return _priority[p];
	}

	#ifndef __sun__
	# define THREADFLAG (THR_NEW_LWP | THR_JOINABLE | THR_SCHED_DEFAULT)
	#else
	# define THREADFLAG (THR_NEW_LWP | THR_JOINABLE)
	#endif

	Thread::Thread() : _task(0), _threadID(0), _threadHandle(0)
	{

	}

	Thread::Thread(Runnable* instance) : _task(instance), _threadID(0), _threadHandle(0)
	{
		// register reference to _task to prevent it deeltion until destructor
		if (_task)
			_task->incReference();

		bool _start = start();
		assert (_start);
	}

	Thread::~Thread()
	{
		//Wait();

		// deleted runnable object (if no other references)
		if (_task)
			_task->decReference();
	}

	//initialize Thread's class static member
	Thread::ThreadStorage Thread::_ThreadStorage;
	ThreadPriority Thread::_threadPriority;

	bool Thread::start()
	{
		if (_task == 0 || _threadID != 0)
			return false;

		bool res = (ACE_Thread::spawn(&Thread::ThreadTask, (void*)_task, THREADFLAG, &_threadID, &_threadHandle) == 0);

		if (res)
			_task->incReference();

		return res;
	}

	bool Thread::wait()
	{
		if (!_threadHandle || !_task)
			return false;

		ACE_THR_FUNC_RETURN _value = ACE_THR_FUNC_RETURN(-1);
		int _res = ACE_Thread::join(_threadHandle, &_value);

		_threadID = 0;
		_threadHandle = 0;

		return (_res == 0);
	}

	void Thread::destroy()
	{
		if (!_threadID || !_task)
			return;

		if (ACE_Thread::kill(_threadID, -1) != 0)
			return;

		_threadID = 0;
		_threadHandle = 0;

		// reference set at ACE_Thread::spawn
		_task->decReference();
	}

	void Thread::suspend()
	{
		ACE_Thread::suspend(_threadHandle);
	}

	void Thread::resume()
	{
		ACE_Thread::resume(_threadHandle);
	}

	ACE_THR_FUNC_RETURN Thread::ThreadTask(void * param)
	{
		Runnable * _task = (Runnable*)param;
		_task->run();

		// task execution complete, free referecne added at
		_task->decReference();

		return (ACE_THR_FUNC_RETURN)0;
	}

	ACE_thread_t Thread::currentId()
	{
		return ACE_Thread::self();
	}

	ACE_hthread_t Thread::currentHandle()
	{
		ACE_hthread_t _handle;
		ACE_Thread::self(_handle);

		return _handle;
	}

	Thread* Thread::current()
	{
		Thread * _thread = _ThreadStorage.ts_object();
		if(!_thread) {
			_thread = new Thread();
			_thread->_threadID = Thread::currentId();
			_thread->_threadHandle = Thread::currentHandle();

			Thread * _oldValue = _ThreadStorage.ts_object(_thread);
			if(_oldValue)
				delete _oldValue;
		}

		return _thread;
	}

	void Thread::setPriority(Priority type)
	{
	#ifndef __sun__
		int _priority = _threadPriority.getPriority(type);
		int _ok = ACE_Thread::setprio(_threadHandle, _priority);
		//remove this ASSERT in case you don't want to know is thread priority change was successful or not
		assert (_ok == 0);
	#endif
	}

	void Thread::Sleep(unsigned long msecs)
	{
		ACE_OS::sleep(ACE_Time_Value(0, 1000 * msecs));
	}
}
